package com.zz.common.codeGenerator.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zz.common.base.dto.PageListResultDto;
import com.zz.common.base.enums.BaseExceptionEnum;
import com.zz.common.base.param.QueryParam;
import com.zz.common.codeGenerator.common.*;
import com.zz.common.codeGenerator.dto.GenFormSchemaDto;
import com.zz.common.codeGenerator.dto.GenTableColumnDto;
import com.zz.common.codeGenerator.dto.GenTableDto;
import com.zz.common.codeGenerator.entity.GenTable;
import com.zz.common.codeGenerator.entity.GenTableColumn;
import com.zz.common.codeGenerator.event.I18nConfigDto;
import com.zz.common.codeGenerator.event.I18nConfigEvent;
import com.zz.common.codeGenerator.mapper.GenTableMapper;
import com.zz.common.codeGenerator.service.GenFormSchemaService;
import com.zz.common.codeGenerator.service.GenTableColumnService;
import com.zz.common.codeGenerator.service.GenTableService;
import com.zz.common.constants.BaseConstant;
import com.zz.common.exception.ExceptionFactory;
import com.zz.common.utils.common.SqlUtil;
import com.zz.common.utils.common.UUIDGenerator;
import com.zz.common.utils.convert.StringFormatUtil;
import com.zz.common.utils.convert.dto.LocaleValue;
import com.zz.common.utils.mapper.BeanMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 代码生成业务表 服务实现类
 *
 * @author zhang.lei
 * @since 2025-08-18 20:31:13
 */
@Service
@Slf4j
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements GenTableService {

    @Resource
    private GenTableMapper genTableMapper;

    @Resource
    private GenTableColumnService genTableColumnService;

    @Resource
    private ApplicationEventPublisher publisher;

    @Resource
    private GenFormSchemaService genFormSchemaService;

    @Override
    public PageListResultDto<GenTableDto> queryWithPage(GenTableDto genTableDto, QueryParam queryParam) {
        Page<GenTable> page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize());
        QueryWrapper<GenTable> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", BaseConstant.NO);
        List<GenTable> genTables = genTableMapper.selectList(page, wrapper);
        List<GenTableDto> genTableDtos = BeanMapper.mapList(genTables, GenTable.class, GenTableDto.class);
        return new PageListResultDto<>((int) page.getCurrent(), (int) page.getTotal(), queryParam.getPageSize(), genTableDtos);
    }

    @Override
    public List<GenTableDto> queryWithNoPage(GenTableDto genTableDto) {
        QueryWrapper<GenTable> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", BaseConstant.NO);
        List<GenTable> genTables = genTableMapper.selectList(wrapper);
        return BeanMapper.mapList(genTables, GenTable.class, GenTableDto.class);
    }

    @Override
    public List<GenTableDto> queryAll() {
        QueryWrapper<GenTable> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", BaseConstant.NO);
        List<GenTable> genTables = genTableMapper.selectList(wrapper);
        return BeanMapper.mapList(genTables, GenTable.class, GenTableDto.class);
    }

    @Override
    public GenTableDto queryById(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        GenTable genTable = genTableMapper.selectById(id);
        GenTableDto map = BeanMapper.map(genTable, GenTableDto.class);
        List<GenTableColumnDto> genTableColumnDtos = genTableColumnService.queryByTableId(map.getTableId());
        map.setColumns(genTableColumnDtos);
        return map;
    }

    @Override
    public List<GenTableDto> queryByIds(List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }
        List<GenTable> genTables = genTableMapper.selectByIds(ids);
        if (CollectionUtils.isEmpty(genTables)) {
            return new ArrayList<>();
        }
        return BeanMapper.mapList(genTables, GenTable.class, GenTableDto.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GenTableDto save(GenTableDto genTableDto) {
        // 根据业务修改
        return insert(genTableDto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<GenTableDto> saveBatch(List<GenTableDto> genTableDtos) {
        // 根据业务修改
        return insertList(genTableDtos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GenTableDto modify(GenTableDto genTableDto, String currentLocale) {
        // 校验修改逻辑
        validateModify(genTableDto);
        // 修改主单
        updateById(genTableDto);

        List<I18nConfigDto> i18nConfigDtos = new ArrayList<>();
        List<GenFormSchemaDto> initSchemas = new ArrayList<>();

        List<GenTableColumnDto> columns = genTableDto.getColumns();
        if (CollectionUtil.isNotEmpty(columns)) {
            String enableI18n = genTableDto.getEnableI18n();
            String tplCategory = genTableDto.getTplCategory();
            String options = genTableDto.getOptions();
            String treeCode = VelocityUtils.getTreecode(JSON.parseObject(options));
            if (BaseConstant.YES.equals(enableI18n)) {
                // 国家化
                for (GenTableColumnDto column : columns) {
                    // 根据是否支持国际化，修改columnShowName字段
                    column.setColumnShowName("proxy.$t('" + genTableDto.getTableName() + "." + column.getColumnName() + "')");

                    // 组装i18n配置
                    i18nConfigDtos.addAll(convertColumnShowNameToI18nConfig(genTableDto, column.getColumnName(),
                            column.getColumnShowNameNew()));
                }
            } else {
                // 不支持国际化
                for (GenTableColumnDto column : columns) {
                    if (GenConstants.TPL_TREE.equals(tplCategory) && column.getJavaField().equals(treeCode)) {
                        column.setColumnShowName("父级" + genTableDto.getFunctionName());
                    }
                    column.setColumnShowName(StringFormatUtil.removeBrackets(column.getColumnComment()));
                }
            }
            List<GenTableColumnDto> modified = genTableColumnService.modifyBatch(columns);

            if (BaseConstant.YES.equals(genTableDto.getDesignerType())) {
                initSchemas.add(initFormSchema(genTableDto, modified));
            }

        }

        // 主子表类型，标记子表中的外键字段
        if (GenConstants.TPL_SUB.equals(genTableDto.getTplCategory())) {
            String subTableName = genTableDto.getSubTableName();
            QueryWrapper<GenTable> genTableQueryWrapper = new QueryWrapper<>();
            genTableQueryWrapper.eq("table_name", subTableName);
            genTableQueryWrapper.eq("del_flag", BaseConstant.NO);
            GenTable genTable = genTableMapper.selectOne(genTableQueryWrapper);
            genTableColumnService.markFkInSubTable(genTable.getTableId(), genTableDto.getSubTableFkName());
        }

        if (BaseConstant.YES.equals(genTableDto.getEnableI18n())) {
            if (!i18nConfigDtos.isEmpty()) {
                addItselfForI18nConfig(i18nConfigDtos, genTableDto);
                publisher.publishEvent(new I18nConfigEvent(i18nConfigDtos));
            }
        } else {
            List<I18nConfigDto> delI18n = buildI18nDeleteConfigs(genTableDto, genTableDto.getColumns());
            if (!delI18n.isEmpty()) {
                addItselfForI18nDelete(delI18n, genTableDto);
                publisher.publishEvent(new I18nConfigEvent(delI18n));
            }
        }

        if (BaseConstant.YES.equals(genTableDto.getDesignerType())) {
            if (!initSchemas.isEmpty()) {
                genFormSchemaService.saveOrUpdateWithTableIdBatch(initSchemas);
            }
        } else {
            genFormSchemaService.trueDeleteByTableIds(Collections.singletonList(genTableDto.getTableId()));
        }

        return genTableDto;
    }

    private void validateModify(GenTableDto genTableDto) {
        if (GenConstants.TPL_TREE.equals(genTableDto.getTplCategory())) {
            String jsonString = JSON.toJSONString(genTableDto.getParams());
            JSONObject jsonObject = JSON.parseObject(jsonString);
            if (StringUtils.isBlank(jsonObject.getString(GenConstants.TREE_CODE))) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "树形表的树编码不能为空");
            } else if (StringUtils.isBlank(jsonObject.getString(GenConstants.TREE_PARENT_CODE))) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "树形表的父编码不能为空");
            } else if (StringUtils.isBlank(jsonObject.getString(GenConstants.TREE_NAME))) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "树形表的树名称不能为空");
            }
        } else if (GenConstants.TPL_SUB.equals(genTableDto.getTplCategory())) {
            if (StringUtils.isEmpty(genTableDto.getSubTableName())) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "子表名不能为空");
            } else if (StringUtils.isEmpty(genTableDto.getSubTableFkName())) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "子表外键不能为空");
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<GenTableDto> modifyBatch(List<GenTableDto> genTableDtos) {
        // 根据业务修改
        return updateList(genTableDtos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String id) {
        GenTableDto genTableDto = new GenTableDto();
        genTableDto.setTableId(id);
        deleteById(genTableDto);
        QueryWrapper<GenTable> genTableQueryWrapper = new QueryWrapper<>();
        genTableQueryWrapper.eq("table_id", id);
        GenTable genTable = genTableMapper.selectOne(genTableQueryWrapper);
        if (genTable != null) {
            GenTableDto full = BeanMapper.map(genTable, GenTableDto.class);
            List<GenTableColumnDto> cols = genTableColumnService.queryByTableId(id);
            full.setColumns(cols);
            List<I18nConfigDto> delI18n = buildI18nDeleteConfigs(full, cols);
            if (!delI18n.isEmpty()) {
                addItselfForI18nDelete(delI18n, full);
                publisher.publishEvent(new I18nConfigEvent(delI18n));
            }
        }

        genFormSchemaService.trueDeleteByTableIds(Collections.singletonList(id));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBatch(List<String> ids) {
        deleteByPrimaryKeys(ids);
        if (CollectionUtils.isNotEmpty(ids)) {
            List<I18nConfigDto> delAll = new ArrayList<>();
            for (String id : ids) {
                QueryWrapper<GenTable> w = new QueryWrapper<>();
                w.eq("table_id", id);
                GenTable genTable = genTableMapper.selectOne(w);
                if (genTable != null) {
                    GenTableDto full = BeanMapper.map(genTable, GenTableDto.class);
                    List<GenTableColumnDto> cols = genTableColumnService.queryByTableId(id);
                    full.setColumns(cols);
                    delAll.addAll(buildI18nDeleteConfigs(full, cols));
                    addItselfForI18nDelete(delAll, full);
                }
            }
            if (!delAll.isEmpty()) {
                publisher.publishEvent(new I18nConfigEvent(delAll));
            }

            genFormSchemaService.trueDeleteByTableIds(ids);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GenTableDto insert(GenTableDto genTableDto) {
        genTableDto.setTableId(UUIDGenerator.generateUUID());
        genTableDto.setDelFlag(BaseConstant.NO);
        GenTable entity = BeanMapper.map(genTableDto, GenTable.class);
        genTableMapper.insert(entity);
        return genTableDto;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GenTableDto insertWithId(GenTableDto genTableDto) {
        if (StringUtils.isBlank(genTableDto.getTableId())) {
            genTableDto.setTableId(UUIDGenerator.generateUUID());
        }
        genTableDto.setDelFlag(BaseConstant.NO);
        GenTable entity = BeanMapper.map(genTableDto, GenTable.class);
        genTableMapper.insert(entity);
        return genTableDto;
    }

    private List<GenTableDto> insertList(List<GenTableDto> genTableDtos) {
        if (CollectionUtils.isEmpty(genTableDtos)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量插入时，参数不能为空");
        }
        List<GenTable> insertList = new ArrayList<>();
        for (GenTableDto genTableDto : genTableDtos) {
            if (StringUtils.isBlank(genTableDto.getTableId())) {
                genTableDto.setTableId(UUIDGenerator.generateUUID());
            }
            genTableDto.setDelFlag(BaseConstant.NO);
            insertList.add(BeanMapper.map(genTableDto, GenTable.class));
        }
        genTableMapper.insert(insertList, 1000);
        return genTableDtos;
    }

    private List<GenTableDto> updateList(List<GenTableDto> genTableDtos) {
        if (CollectionUtils.isEmpty(genTableDtos)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量更新时，参数不能为空");
        }
        for (GenTableDto genTableDto : genTableDtos) {
            if (StringUtils.isBlank(genTableDto.getTableId())) {
                throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "批量更新时，主键不能为空");
            }
        }
        List<GenTable> genTableList = BeanMapper.mapList(genTableDtos, GenTableDto.class, GenTable.class);
        genTableMapper.updateById(genTableList, 1000);
        return genTableDtos;
    }

    @Transactional(rollbackFor = Exception.class)
    public int updateById(GenTableDto genTableDto) {
        if (StringUtils.isBlank(genTableDto.getTableId())) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "更新时，主键不能为空");
        }
        Map<String, Object> params = new HashMap<>();
        for (Map.Entry<String, Object> entry : genTableDto.getParams().entrySet()) {
            if (entry.getValue() != null && ObjectUtils.isNotEmpty(entry.getValue())) {
                params.put(entry.getKey(), entry.getValue());
            }
        }
        String options = JSON.toJSONString(params);
        genTableDto.setOptions(options);
        GenTable entity = BeanMapper.map(genTableDto, GenTable.class);
        return genTableMapper.updateById(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    public int trueDeleteById(String id) {
        return genTableMapper.deleteById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public int deleteById(GenTableDto genTableDto) {
        genTableDto.setDelFlag(BaseConstant.YES);
        return updateById(genTableDto);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteByPrimaryKeys(List<String> ids) {
        List<GenTableDto> genTableDtos = new ArrayList<>();
        for (String id : ids) {
            GenTableDto genTableDto = new GenTableDto();
            genTableDto.setTableId(id);
            genTableDto.setDelFlag(BaseConstant.YES);
            genTableDtos.add(genTableDto);
        }
        updateList(genTableDtos);
    }

    @Override
    public PageListResultDto<GenTableDto> selectDbTables(GenTableDto genTableDto, QueryParam queryParam) {
        Page<GenTable> page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize());
        List<GenTable> result = genTableMapper.selectDbTableList(page, genTableDto);
        // 查询数据表信息（过滤掉qrtz_\gen_开头的表）
        List<GenTableDto> genTableDtos = BeanMapper.mapList(result, GenTable.class, GenTableDto.class);
        return new PageListResultDto<>((int) page.getCurrent(), (int) page.getTotal(), queryParam.getPageSize(), genTableDtos);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createTableBySql(String sql, String createTableBySql) {
        try {
            // 1. SQL安全检查
            SqlUtil.filterKeyword(sql);
            // 2. 解析SQL语句
            List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
            List<String> tableNames = new ArrayList<>();
            // 3. 遍历SQL语句，找出CREATE TABLE语句
            for (SQLStatement sqlStatement : sqlStatements) {
                if (sqlStatement instanceof MySqlCreateTableStatement) {
                    MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
                    // 4. 执行建表操作
                    if (createTable(createTableStatement.toString())) {
                        String tableName = createTableStatement.getTableName().replaceAll("`", "");
                        tableNames.add(tableName);
                    }
                }
            }

            // 5. 查询新建的表信息
            List<GenTable> genTables = selectDbTableListByNames(tableNames);
            // 6. 导入到代码生成器
            importGenTable(genTables, GenConfig.getAuthor(), createTableBySql);
        } catch (Exception e) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.FAIL, e.getMessage());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importTable(List<String> tableNames, String currentLocale) {
        List<GenTable> genTables = selectDbTableListByNames(tableNames);
        importGenTable(genTables, GenConfig.getAuthor(), currentLocale);
    }

    @Override
    public Map<String, String> preview(String tableId) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        // 查询表信息
        GenTableDto genTableDto = queryById(tableId);
        List<GenTableColumnDto> genTableColumnDtos = genTableColumnService.queryByTableId(tableId);
        genTableDto.setColumns(genTableColumnDtos);

        // 设置主子表信息
        setSubTable(genTableDto);

        // 设置主键列信息
        setPkColumn(genTableDto);

        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(genTableDto);
        GenFormSchemaDto schemaDto = genFormSchemaService.queryLatestByTableId(tableId);
        if (schemaDto != null && schemaDto.getSchemaJson() != null) {
            context.put("formSchema", schemaDto.getSchemaJson());
        }

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(genTableDto);
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return dataMap;
    }

    @Override
    public void synchDb(String tableName, String currentLocale) {
        GenTableDto genTableDto = selectGenTableByName(tableName);
        List<GenTableColumnDto> columns = genTableDto.getColumns();
        Map<String, GenTableColumnDto> tableColumnDtoMap = columns.stream().collect(Collectors.toMap(GenTableColumnDto::getColumnName, Function.identity()));
        List<GenTableColumn> dbTableColumns = genTableColumnService.selectDbTableColumnsByName(tableName);
        if (CollectionUtil.isEmpty(dbTableColumns)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.NOT_FOUND, "表" + tableName + "不存在");
        }
        List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
        dbTableColumns.forEach(column -> {
            GenUtils.initColumnField(column, BeanMapper.map(genTableDto, GenTable.class), currentLocale);
            column.setDelFlag(BaseConstant.NO);
            if (tableColumnDtoMap.containsKey(column.getColumnName())) {
                GenTableColumnDto prevColumn = tableColumnDtoMap.get(column.getColumnName());
                column.setColumnId(prevColumn.getColumnId());
                if (BaseConstant.YES.equals(column.getIsList())) {
                    // 如果是列表，继续保留查询方式/字典类型选项
                    column.setDictType(prevColumn.getDictType());
                    column.setQueryType(prevColumn.getQueryType());
                }
                if (StringUtils.isNotBlank(prevColumn.getIsRequired()) && BaseConstant.NO.equals(column.getIsPk())
                        && (BaseConstant.YES.equals(column.getIsInsert()) || BaseConstant.YES.equals(column.getIsEdit()))) {
                    // 如果是(新增/修改&非主键/非忽略及父属性)，继续保留必填/显示类型选项
                    column.setIsRequired(prevColumn.getIsRequired());
                    column.setHtmlType(prevColumn.getHtmlType());
                }
                genTableColumnService.updateById(column);
            } else {
                genTableColumnService.insert(BeanMapper.map(column, GenTableColumnDto.class));
            }
        });

        List<GenTableColumnDto> delColumns = columns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(delColumns)) {
            List<String> collect = delColumns.stream().map(GenTableColumnDto::getTableId).collect(Collectors.toList());
            genTableColumnService.batchTrueDeleteByTableIds(collect);
            List<I18nConfigDto> delI18n = buildI18nDeleteConfigs(genTableDto, delColumns);
            if (!delI18n.isEmpty()) {
                addItselfForI18nDelete(delI18n, genTableDto);
                publisher.publishEvent(new I18nConfigEvent(delI18n));
            }
        }
    }

    @Override
    public void batchGenCode(List<String> tableIds) throws IOException {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        byte[] data = downloadCode(tableIds);

        if (data != null && response != null) {
            genCode(request, response, data);
        }
    }

    @Override
    public void batchTureDelete(List<String> ids) {
        QueryWrapper<GenTable> genTableQueryWrapper = new QueryWrapper<>();
        genTableQueryWrapper.in("table_id", ids);
        List<GenTable> toDelete = genTableMapper.selectList(genTableQueryWrapper);
        if (CollectionUtils.isNotEmpty(toDelete)) {
            List<I18nConfigDto> delAll = new ArrayList<>();
            for (GenTable gt : toDelete) {
                GenTableDto full = BeanMapper.map(gt, GenTableDto.class);
                List<GenTableColumnDto> cols = genTableColumnService.queryByTableId(gt.getTableId());
                full.setColumns(cols);
                delAll.addAll(buildI18nDeleteConfigs(full, cols));
                addItselfForI18nDelete(delAll, full);
            }
            if (!delAll.isEmpty()) {
                publisher.publishEvent(new I18nConfigEvent(delAll));
            }
        }
        genTableMapper.delete(genTableQueryWrapper);
        genTableColumnService.batchTrueDeleteByTableIds(ids);

        genFormSchemaService.trueDeleteByTableIds(ids);
    }

    @Override
    public List<GenTableColumnDto> queryColumnsByTableName(String tableName) {
        if (StringUtils.isBlank(tableName)) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.INTERNAL_SERVER_ERROR, "表名不能为空");
        }
        QueryWrapper<GenTable> genTableQueryWrapper = new QueryWrapper<>();
        genTableQueryWrapper.eq("table_name", tableName);
        genTableQueryWrapper.eq("del_flag", BaseConstant.NO);
        GenTable genTable = genTableMapper.selectOne(genTableQueryWrapper);
        if (genTable == null) {
            throw ExceptionFactory.createBaseException(BaseExceptionEnum.NOT_FOUND, "表" + tableName + "不存在");
        }
        String tableId = genTable.getTableId();
        return genTableColumnService.queryByTableId(tableId);
    }

    private boolean createTable(String sql) {
        return genTableMapper.createTable(sql) == 0;
    }

    private List<GenTable> selectDbTableListByNames(List<String> tableNames) {
        String[] tableName = tableNames.toArray(new String[tableNames.size()]);
        return genTableMapper.selectDbTableListByNames(tableName);
    }

    public void importGenTable(List<GenTable> tableList, String operName, String currentLocale) {

        List<I18nConfigDto> i18nConfigDtos = new ArrayList<>();
        List<GenTableColumnDto> savedColumns = new ArrayList<>();
        List<GenFormSchemaDto> initSchemas = new ArrayList<>();
        for (GenTable genTable : tableList) {
            String tableName = genTable.getTableName();
            // 初始化表信息
            GenUtils.initTable(genTable, operName);
            genTable.setTableId(UUIDGenerator.generateUUID());
            genTable.setDelFlag(BaseConstant.NO);

            // 保存表信息
            int row = genTableMapper.insert(genTable);
            if (row > 0) {
                // 保存列信息
                List<GenTableColumn> genTableColumns = genTableColumnService.selectDbTableColumnsByName(tableName);
                for (GenTableColumn column : genTableColumns) {
                    // 字段信息初始化
                    GenUtils.initColumnField(column, genTable, currentLocale);
                    column.setDelFlag(BaseConstant.NO);
                    GenTableColumnDto saved = genTableColumnService.save(BeanMapper.map(column, GenTableColumnDto.class));

                    // 组装formSchema参数
                    savedColumns.add(saved);

                    // 组装国际化参数
                    if (BaseConstant.YES.equals(genTable.getEnableI18n())) {
                        i18nConfigDtos.addAll(convertColumnShowNameToI18nConfig(BeanMapper.map(genTable, GenTableDto.class), column.getColumnName(),
                                column.getColumnShowNameNew()));
                    }

                }
            }

            addItselfForI18nConfig(i18nConfigDtos, BeanMapper.map(genTable, GenTableDto.class));
            if (BaseConstant.YES.equals(genTable.getDesignerType())) {
                initSchemas.add(initFormSchema(BeanMapper.map(genTable, GenTableDto.class), savedColumns));
            }
        }

        // 保存formSchema
        if (!initSchemas.isEmpty()) {
            genFormSchemaService.saveOrUpdateWithTableIdBatch(initSchemas);
        }

        // 发消息
        if (!i18nConfigDtos.isEmpty()) {
            log.info("send I18nConfigEvent :" + i18nConfigDtos.toString());
            publisher.publishEvent(new I18nConfigEvent(i18nConfigDtos));
        }
    }

    public void setSubTable(GenTableDto table) {
        String subTableName = table.getSubTableName();
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(subTableName)) {
            table.setSubTable(selectGenTableByName(subTableName));
        }
    }

    private GenTableDto selectGenTableByName(String tableName) {
        QueryWrapper<GenTable> genTableQueryWrapper = new QueryWrapper<>();
        genTableQueryWrapper.eq("table_name", tableName);
        GenTable genTable = genTableMapper.selectOne(genTableQueryWrapper);
        GenTableDto map = BeanMapper.map(genTable, GenTableDto.class);
        List<GenTableColumnDto> genTableColumnDtos = genTableColumnService.queryByTableId(map.getTableId());
        map.setColumns(genTableColumnDtos);
        return map;
    }

    private void setPkColumn(GenTableDto genTableDto) {
        for (GenTableColumnDto column : genTableDto.getColumns()) {
            if (BaseConstant.YES.equals(column.getIsPk())) {
                genTableDto.setPkColumn(column);
                break;
            }
        }
        if (genTableDto.getPkColumn() == null) {
            genTableDto.setPkColumn(genTableDto.getColumns().get(0));
        }
        if (GenConstants.TPL_SUB.equals(genTableDto.getTplCategory())) {
            for (GenTableColumnDto column : genTableDto.getSubTable().getColumns()) {
                if (BaseConstant.YES.equals(column.getIsPk())) {
                    genTableDto.getSubTable().setPkColumn(column);
                    break;
                }
            }
            if (genTableDto.getSubTable().getPkColumn() == null) {
                genTableDto.getSubTable().setPkColumn(genTableDto.getSubTable().getColumns().get(0));
            }
        }
    }

    private byte[] downloadCode(List<String> tableIds) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableId : tableIds) {
            generatorCode(tableId, zip);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    private void generatorCode(String tableId, ZipOutputStream zip) {
        // 查询表信息
        GenTableDto genTableDto = queryById(tableId);
        List<GenTableColumnDto> genTableColumnDtos = genTableColumnService.queryByTableId(tableId);
        genTableDto.setColumns(genTableColumnDtos);
        // 设置主子表信息
        setSubTable(genTableDto);

        // 设置主键列信息
        setPkColumn(genTableDto);

        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(genTableDto);
        GenFormSchemaDto schemaDto = genFormSchemaService.queryLatestByTableId(tableId);
        if (schemaDto != null && schemaDto.getSchemaJson() != null) {
            context.put("formSchema", schemaDto.getSchemaJson());
        }

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(genTableDto);
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);
            try {
                // 添加到zip
                System.out.println(VelocityUtils.getFileName(template, genTableDto));
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, genTableDto)));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + genTableDto.getTableName(), e);
            }
        }
    }

    private void genCode(HttpServletRequest request, HttpServletResponse response, byte[] data) throws IOException {
        Map<String, String> result = setCharsetAndDownloadFileName("genCode.zip", request);
        String charset = result.get("charset");
        String downloadFileName = result.get("downloadFileName");

        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=" + charset);
        response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
        IOUtils.write(data, response.getOutputStream());
    }

    // 浏览器适配
    private static Map<String, String> setCharsetAndDownloadFileName(String fileName, HttpServletRequest request) throws UnsupportedEncodingException {
        Map<String, String> result = new HashMap<>();
        // 获取HTTP请求的"USER-AGENT"头部信息，用于判断浏览器类型
        String agent = request.getHeader("USER-AGENT");
        String charset;
        String downloadFileName;
        if (null != agent) {
            if (-1 != agent.indexOf("Firefox")) {
                // 如果"USER-AGENT"头部信息中包含"Firefox"，则设置字符编码为UTF-8，使用Base64编码对文件名进行处理，
                // 最后拼接成"=?UTF-8?B?" + 编码后的文件名 + "?="的格式。
                charset = "UTF-8";
                downloadFileName = "=?UTF-8?B?" + Base64.getEncoder().encodeToString(fileName.getBytes("UTF-8")) + "?=";
            } else if (-1 != agent.indexOf("Chrome")) {
                // 如果"USER-AGENT"头部信息中包含"Chrome"，则设置字符编码为ISO8859-1，使用ISO8859-1编码对文件名进行处理
                charset = "ISO8859-1";
                downloadFileName = new String(fileName.getBytes(), "ISO8859-1");
            } else {
                // 如果以上两个条件都不满足，则设置字符编码为UTF-8，使用URL编码对文件名进行处理，并将"+"替换为"%20"
                charset = "UTF-8";
                downloadFileName = URLEncoder.encode(fileName, "UTF-8");
                downloadFileName = downloadFileName.replace("+", "%20");
            }
        } else {
            // 如果"USER-AGENT"头部信息为null，则设置字符编码为UTF-8，将下载文件名设置为原始文件名。
            charset = "UTF-8";
            downloadFileName = fileName;
        }
        result.put("charset", charset);
        result.put("downloadFileName", downloadFileName);
        return result;
    }

    private List<I18nConfigDto> convertColumnShowNameToI18nConfig(GenTableDto genTableDto, String columnName, Map<String, Object> columnShowNameMap) {

        List<I18nConfigDto> i18nConfigDtos = new ArrayList<>();

        String tableName = genTableDto.getTableName();
        String options = genTableDto.getOptions();
        JSONObject paramsObj = JSON.parseObject(options);
        String treeParentCode = VelocityUtils.getTreeParentCode(paramsObj);

        ObjectMapper objectMapper = new ObjectMapper();
        LocaleValue localeValue = objectMapper.convertValue(columnShowNameMap, LocaleValue.class);
        Map<String, String> values = localeValue.getValues();
        values.forEach((k, v) -> {
            String value = v;
            if (GenConstants.TPL_TREE.equals(genTableDto.getTplCategory()) && StringFormatUtil.toCamelCase(columnName).equals(treeParentCode)) {
                value = k.equals("zh-CN") ? "父级" + genTableDto.getFunctionName() : "Top " + genTableDto.getBusinessName();
            }

            I18nConfigDto i18nConfigDto = new I18nConfigDto(UUIDGenerator.generateUUID(),
                    k, tableName, columnName, value, "", BaseConstant.YES, BaseConstant.NO,
                    null, null);
            i18nConfigDtos.add(i18nConfigDto);
        });

        return i18nConfigDtos;
    }

    private void addItselfForI18nConfig(List<I18nConfigDto> i18nConfigDtos, GenTableDto genTableDto) {
        i18nConfigDtos.add(new I18nConfigDto(UUIDGenerator.generateUUID(),
                "zh-CN", genTableDto.getTableName(), "itself", genTableDto.getFunctionName(), "", BaseConstant.YES, BaseConstant.NO,
                null, null));
        i18nConfigDtos.add(new I18nConfigDto(UUIDGenerator.generateUUID(),
                "en-US", genTableDto.getTableName(), "itself", genTableDto.getBusinessName(), "", BaseConstant.YES, BaseConstant.NO,
                null, null));
    }

    private List<I18nConfigDto> convertColumnDeleteToI18nConfig(GenTableDto genTableDto, String columnName, Map<String, Object> columnShowNameMap) {
        List<I18nConfigDto> i18nConfigDtos = new ArrayList<>();
        String tableName = genTableDto.getTableName();
        ObjectMapper objectMapper = new ObjectMapper();
        LocaleValue localeValue = objectMapper.convertValue(columnShowNameMap, LocaleValue.class);
        Map<String, String> values = localeValue != null ? localeValue.getValues() : new HashMap<>();
        if (values.isEmpty()) {
            values.put("zh-CN", "");
            values.put("en-US", "");
        }
        values.forEach((k, v) -> {
            I18nConfigDto i18nConfigDto = new I18nConfigDto(UUIDGenerator.generateUUID(),
                    k, tableName, columnName, "", "", BaseConstant.YES, BaseConstant.YES,
                    null, null);
            i18nConfigDtos.add(i18nConfigDto);
        });
        return i18nConfigDtos;
    }

    private List<I18nConfigDto> buildI18nDeleteConfigs(GenTableDto genTableDto, List<GenTableColumnDto> targetColumns) {
        List<I18nConfigDto> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(targetColumns)) {
            for (GenTableColumnDto column : targetColumns) {
                list.addAll(convertColumnDeleteToI18nConfig(genTableDto, column.getColumnName(), column.getColumnShowNameNew()));
            }
        }
        return list;
    }

    private void addItselfForI18nDelete(List<I18nConfigDto> i18nConfigDtos, GenTableDto genTableDto) {
        i18nConfigDtos.add(new I18nConfigDto(UUIDGenerator.generateUUID(),
                "zh-CN", genTableDto.getTableName(), "itself", "", "", BaseConstant.YES, BaseConstant.YES,
                null, null));
        i18nConfigDtos.add(new I18nConfigDto(UUIDGenerator.generateUUID(),
                "en-US", genTableDto.getTableName(), "itself", "", "", BaseConstant.YES, BaseConstant.YES,
                null, null));
    }

    private GenFormSchemaDto initFormSchema(GenTableDto genTableDto, List<GenTableColumnDto> genTableColumnDtos) {
        GenFormSchemaDto genFormSchemaDto = new GenFormSchemaDto();
        genFormSchemaDto.setTableId(genTableDto.getTableId());
        genFormSchemaDto.setTableName(genTableDto.getTableName());
        Map<String, Object> schema = new LinkedHashMap<>();

        Map<String, Object> formConfig = new LinkedHashMap<>();
        formConfig.put("title", genTableDto.getFunctionName());
        formConfig.put("labelWidth", "120px");
        formConfig.put("labelPosition", "right");
        formConfig.put("size", "default");
        formConfig.put("showSubmitButton", Boolean.TRUE);
        formConfig.put("showResetButton", Boolean.FALSE);
        formConfig.put("events", new LinkedHashMap<>());
        schema.put("formConfig", formConfig);

        List<Map<String, Object>> formItems = new ArrayList<>();

        List<Map<String, Object>> itemBuffer = new ArrayList<>();

        for (GenTableColumnDto column : genTableColumnDtos) {
            if (column == null) continue;
            if ("1".equals(column.getIsPk()) && "1".equals(column.getIsIncrement())) {
                continue;
            }
            if (column.isSuperColumn()) {
                continue;
            }
            if (!"1".equals(column.getIsInsert())) {
                continue;
            }

            Map<String, Object> item = new LinkedHashMap<>();
            String mappedType = mapHtmlType(column.getHtmlType(), column.getJavaType());
            item.put("type", mappedType);
            item.put("key", "field_" + System.currentTimeMillis());
            item.put("field", column.getJavaField());
            item.put("label", resolveLabel(genTableDto, column));
            String labelText = String.valueOf(item.get("label"));
            if ("1".equals(column.getIsRequired())) {
                item.put("required", Boolean.TRUE);
            }
            if ("select".equals(mappedType) || "radio".equals(mappedType) || "checkbox".equals(mappedType)) {
                item.put("options", new ArrayList<>());
            }
            if ("date".equals(mappedType)) {
                item.put("dateType", "date");
            }
            if ("datetime".equals(mappedType)) {
                // 日期时间格式在表单设计器中type业务date
                item.put("type", "date");
                item.put("dateType", "datetime");
                item.put("format", "YYYY-MM-DD HH:mm:ss");
            }
            if ("number".equals(mappedType)) {
                if (GenConstants.TYPE_LONG.equals(column.getJavaType()) || GenConstants.TYPE_INTEGER.equals(column.getJavaType())) {
                    item.put("min", 0);
                    item.put("max", 100);
                    item.put("step", 1);
                    item.put("precision", 0);
                }
                if (GenConstants.TYPE_DOUBLE.equals(column.getJavaType()) || GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
                    item.put("min", 0);
                    item.put("max", 100);
                    item.put("step", 0.01);
                    item.put("precision", 2);
                }
            }
            if ("time".equals(mappedType)) {
                item.put("format", "HH:mm:ss");
            }

            if (!isBlank(labelText)) {
                if ("input".equals(mappedType) || "number".equals(mappedType) || "textarea".equals(mappedType)) {
                    item.put("placeholder", "请输入" + labelText);
                } else if ("select".equals(mappedType) || "cascader".equals(mappedType) || "treeselect".equals(mappedType) || "date".equals(mappedType) || "datetime".equals(mappedType) || "time".equals(mappedType)) {
                    item.put("placeholder", "请选择" + labelText);
                }
            }

            item.put("disabled", Boolean.FALSE);
            item.put("clearable", Boolean.TRUE);

            itemBuffer.add(item);
        }
        for (int i = 0; i < itemBuffer.size(); i += 2) {
            Map<String, Object> grid = new LinkedHashMap<>();
            grid.put("type", "grid");
            grid.put("gutter", 0);
            grid.put("key", "field_" + System.currentTimeMillis());
            grid.put("label", "栅格布局");
            grid.put("field", "field_" + System.currentTimeMillis());
            grid.put("required", Boolean.FALSE);
            grid.put("disabled", Boolean.FALSE);

            List<Map<String, Object>> columns = new ArrayList<>();

            Map<String, Object> col1 = new LinkedHashMap<>();
            col1.put("span", 12);
            col1.put("children", List.of(itemBuffer.get(i)));
            columns.add(col1);

            if (i + 1 < itemBuffer.size()) {
                Map<String, Object> col2 = new LinkedHashMap<>();
                col2.put("span", 12);
                col2.put("children", List.of(itemBuffer.get(i + 1)));
                columns.add(col2);
            }

            grid.put("columns", columns);
            formItems.add(grid);
        }
        schema.put("formItems", formItems);

        genFormSchemaDto.setSchemaJson(schema);
        genFormSchemaDto.setVersion("1");
        return genFormSchemaDto;
    }

    private String mapHtmlType(String htmlType, String javaType) {
        if (htmlType == null) return "input";
        String type = htmlType.toLowerCase();
        switch (type) {
            case "input":
            case "text":
                return "input";
            case "textarea":
                return "textarea";
            case "select":
                return "select";
            case "radio":
                return "radio";
            case "checkbox":
                return "checkbox";
            case "date":
                return "date";
            case "datetime":
                return "datetime";
            case "time":
                return "time";
            case "number":
                return "number";
            case "switch":
                return "switch";
            case "cascader":
                return "cascader";
            case "tree":
                return "tree";
            case "treeselect":
                return "treeselect";
            case "slider":
                return "slider";
            default:
                // 根据java类型兜底
                if (javaType != null) {
                    String jt = javaType.toLowerCase();
                    if (jt.contains("int") || jt.contains("long") || jt.contains("decimal") || jt.contains("bigdecimal")) {
                        return "number";
                    }
                    if (jt.contains("date") || jt.contains("time")) {
                        return "date";
                    }
                }
                return "input";
        }
    }

    private String defaultLabel(GenTableDto genTableDto, GenTableColumnDto column) {
        Map<String, Object> columnShowNameNew = column.getColumnShowNameNew();

//        String label = column.getColumnShowName();
        String label = column.getColumnComment();
        if (label == null || label.trim().isEmpty()) {
            label = column.getColumnComment();
        }
        if (label == null || label.trim().isEmpty()) {
            label = column.getColumnName();
        }
        return label;
    }

    private String resolveLabel(GenTableDto genTableDto, GenTableColumnDto column) {
        Map<String, Object> columnShowNameNew = column.getColumnShowNameNew();
        String label = null;
        if (columnShowNameNew != null) {
            Object valuesObj = columnShowNameNew.get("values");
            if (valuesObj instanceof Map) {
                Map<?, ?> values = (Map<?, ?>) valuesObj;
                String defaultLocale = String.valueOf(columnShowNameNew.getOrDefault("defaultLocale", "zh_CN"));
                String prefer = normalizeToHyphen(defaultLocale);
                label = getLocaleValue(values, prefer);
                if (isBlank(label)) label = getLocaleValue(values, "zh-CN");
                if (isBlank(label)) label = getLocaleValue(values, "zh_CN");
                if (isBlank(label)) label = getLocaleValue(values, "en-US");
                if (isBlank(label)) label = getLocaleValue(values, "en_US");
                if (isBlank(label) && values != null && !values.isEmpty()) {
                    Object first = values.values().iterator().next();
                    label = first != null ? String.valueOf(first) : null;
                }
            }
        }
        if (isBlank(label)) label = column.getColumnComment();
        if (isBlank(label)) label = column.getColumnName();
        return label;
    }

    private String normalizeToHyphen(String locale) {
        if (locale == null) return null;
        String l = locale.replace('_', '-');
        if ("zh-cn".equalsIgnoreCase(l)) return "zh-CN";
        if ("en-us".equalsIgnoreCase(l)) return "en-US";
        return l;
    }

    private boolean isBlank(String s) {
        return s == null || s.trim().isEmpty();
    }

    private String getLocaleValue(Map<?, ?> values, String key) {
        if (key == null || values == null) return null;
        Object v = values.get(key);
        if (v == null) {
            String alt = key.replace('-', '_');
            v = values.get(alt);
        }
        return v != null ? String.valueOf(v) : null;
    }
}
